package helper.scxml.scxml2.t0.case1_cycle.zone

import helper.base.A2LHM
import helper.base.A2Tuple
import helper.base.ConstraintHelper.CompositeConstraint.Expand.toW1N2Constraint
import helper.base.LHMHelper.A3LHM
import helper.base.LHMHelper.A3LHM.Companion.addAll
import helper.base.LHMHelper.A4LHM
import helper.base.RandomHelper.getRandomInt
import helper.base.RangeHelper.Expand.getRandomObj
import helper.base.RangeTuple
import helper.base.RangeTupleListTuple
import helper.base.TupleHelper.A2TupleExpand.getIntRangeLength
import helper.scxml.scxml2.t0.case1_cycle.zone.ZoneStrategyHelper.EnvRenStrategyArray.Companion.getType1GetAgentZoneStrategyArray
import helper.scxml.scxml2.t0.case1_cycle.zone.ZoneStrategyHelper.TimeEventTuple
import helper.scxml.scxml2.t0.case1_cycle.zone.ZoneStrategyHelper.TimeEventTuple.Companion.getTimeEventTuple
import helper.scxml.scxml2.t0.case1_cycle.zone.ZoneStrategyHelper.Type1GetTimeEventChoiceFunUnit.Companion.getType1GetTimeEventChoiceFunUnit
import helper.scxml.scxml2.zone.t2.ConfigHelper.Config
import helper.scxml.strategy.StrategyHelper.TimedEventHelper.TimedEvent
import helper.scxml.strategy.StrategyHelper.TimedEventHelper.TimedEvent.Companion.makeTimedEvent
import helper.scxml.strategy.TotalStrategyHelper.IZoneTotalStrategy

//ZoneState字符串，Constraint字符串，事件字符串，权重
typealias ZoneStateConstraintEventWeightLHM = A4LHM<String, String, String, Double>
//
typealias TimeEventRangeTuple = RangeTuple<TimeEventTuple>
typealias TimeEventRangeTupleListTuple = RangeTupleListTuple<TimeEventTuple>
//
typealias LocationRangeTimeEventTupleListTupleLHM = A2LHM<String, TimeEventRangeTupleListTuple>

object ZoneStrategyHelper {
    data class TimeEventTuple(
        val timeIntPair: Pair<Int, Int>,
        val event: String,
    ) {
        companion object {
            fun getTimeEventTuple(
                timeIntPair: Pair<Int, Int>,
                event: String,
            ): TimeEventTuple {
                return TimeEventTuple(
                    timeIntPair,
                    event,
                )
            }
        }

        fun toTimeEventChoice(): TimedEvent {
            return makeTimedEvent(
                getRandomInt(
                    this.timeIntPair,
                ),
                this.event,
            )
        }
    }

    class AgentZoneStrategy(
        val a4LHM: ZoneStateConstraintEventWeightLHM,
    ) {
        companion object {
            fun getAgentZoneStrategy(
                a4LHM: ZoneStateConstraintEventWeightLHM,
            ): AgentZoneStrategy {
                return AgentZoneStrategy(
                    a4LHM,
                )
            }
        }
    }

    interface IAgentZoneStrategyArray {
        fun getAgentZoneStrategyArray(
        ): Array<AgentZoneStrategy>
    }

    class EnvRenStrategyArray(
        val envZoneStrategy: AgentZoneStrategy,
        val renZoneStrategy: AgentZoneStrategy,
    ) : IAgentZoneStrategyArray {
        companion object {
            fun getType1GetAgentZoneStrategyArray(
                envZoneStrategy: AgentZoneStrategy,
                renZoneStrategy: AgentZoneStrategy,
            ): EnvRenStrategyArray {
                return EnvRenStrategyArray(
                    envZoneStrategy,
                    renZoneStrategy,
                )
            }
        }

        override fun getAgentZoneStrategyArray(
        ): Array<AgentZoneStrategy> {
            return arrayOf(
                envZoneStrategy,
                renZoneStrategy,
            )
        }
    }

    interface IGetTimeEventChoiceFunUnit {
        fun getGetTimeEventChoiceFun(
        ): (Config, IGetTimeEventChoiceFunUnit) -> TimedEvent

        fun getAdjuvantLHM(
            ifReGet: Boolean,
        ): LocationRangeTimeEventTupleListTupleLHM

        fun getTimeEventChoice(
            zs: Config,
        ): TimedEvent {
            return this.getGetTimeEventChoiceFun()(
                zs,
                this,
            )
        }
    }

    abstract class WithStrategyArrayGetTimeEventChoiceFunUnit(
        private val aAgentZoneStrategyArray: IAgentZoneStrategyArray,
    ) : IGetTimeEventChoiceFunUnit {
        val aStrategyArray: Array<AgentZoneStrategy>
            get() = aAgentZoneStrategyArray.getAgentZoneStrategyArray()
    }

    class Type1GetTimeEventChoiceFunUnit(
        aAgentZoneStrategyArray: IAgentZoneStrategyArray,
        private val aGetTimeEventChoiceFun: (Config, IGetTimeEventChoiceFunUnit) -> TimedEvent,
    ) : WithStrategyArrayGetTimeEventChoiceFunUnit(
        aAgentZoneStrategyArray,
    ) {
        companion object {
            fun getType1GetTimeEventChoiceFunUnit(
                aAgentZoneStrategyArray: IAgentZoneStrategyArray,
                aGetTimeEventChoiceFun: (Config, IGetTimeEventChoiceFunUnit) -> TimedEvent,
            ): Type1GetTimeEventChoiceFunUnit {
                return Type1GetTimeEventChoiceFunUnit(
                    aAgentZoneStrategyArray,
                    aGetTimeEventChoiceFun,
                )
            }
        }

        override fun getGetTimeEventChoiceFun(
        ): (Config, IGetTimeEventChoiceFunUnit) -> TimedEvent {
            return aGetTimeEventChoiceFun
        }

        private var privateAdjuvantLHM: LocationRangeTimeEventTupleListTupleLHM? = null

        override fun getAdjuvantLHM(
            ifReGet: Boolean,
        ): LocationRangeTimeEventTupleListTupleLHM {
            if (!ifReGet && privateAdjuvantLHM != null) {
                return privateAdjuvantLHM!!
            }
            privateAdjuvantLHM = A2LHM()
            val a1Set = HashSet<String>()
            aStrategyArray.map {
                a1Set.addAll(
                    it.a4LHM.keys
                )
            }
            a1Set.forEach { zoneStateString: String ->
                val list = ArrayList<TimeEventRangeTuple>()
                //rangeString,event,weight
                val a3LHM = A3LHM<String, String, Double>()
                aStrategyArray.map {
                    it.a4LHM.map { (a1, a234) ->
                        if (a1 == zoneStateString) {
                            a3LHM.addAll(a234)
                        }
                    }
                }
                var cumulativeWeight = 0.0
                a3LHM.forEach { (rangeString, eventMap) ->
                    val minMaxIntPair = rangeString.toW1N2Constraint().getMinMaxIntPair()
                    val rangeLength = minMaxIntPair.getIntRangeLength()
                    eventMap.forEach { (event, weight) ->
                        val weightedRangeLength = rangeLength * weight
                        cumulativeWeight += weightedRangeLength
                        list.add(
                            A2Tuple(
                                (cumulativeWeight - weightedRangeLength)..cumulativeWeight,
                                getTimeEventTuple(
                                    minMaxIntPair,
                                    event,
                                ),
                            )
                        )
                    }
                }
                privateAdjuvantLHM!![zoneStateString] = A2Tuple(list, cumulativeWeight)
            }
            return privateAdjuvantLHM!!
        }
    }

    open class TotalZoneStrategy(
        val aGetTimeEventChoiceFunUnit: IGetTimeEventChoiceFunUnit,
    ) : IZoneTotalStrategy {
        companion object {
            fun getTotalZoneStrategy(
                aGetTimeEventChoiceFunUnit: IGetTimeEventChoiceFunUnit,
            ): TotalZoneStrategy {
                return TotalZoneStrategy(
                    aGetTimeEventChoiceFunUnit,
                )
            }

            fun getTotalZoneStrategy(
                envZoneStrategy: AgentZoneStrategy,
                renZoneStrategy: AgentZoneStrategy,
                aGetTimeEventChoiceFun: (Config, IGetTimeEventChoiceFunUnit) -> TimedEvent,
                aGetTimeEventChoiceFunUnitFun: (
                    IAgentZoneStrategyArray,
                    (Config, IGetTimeEventChoiceFunUnit) -> TimedEvent,
                ) -> IGetTimeEventChoiceFunUnit,
            ): TotalZoneStrategy {
                val array = getType1GetAgentZoneStrategyArray(
                    envZoneStrategy,
                    renZoneStrategy,
                )
                return getTotalZoneStrategy(
                    aGetTimeEventChoiceFunUnitFun(
                        array,
                        aGetTimeEventChoiceFun,
                    ),
                )
            }

            fun getType1TotalZoneStrategy(
                envZoneStrategy: AgentZoneStrategy,
                renZoneStrategy: AgentZoneStrategy,
                aZSStringFun: (Config) -> String,
                aGetTimeEventChoiceFunUnitFun: (
                    IAgentZoneStrategyArray,
                    (Config, IGetTimeEventChoiceFunUnit) -> TimedEvent,
                ) -> IGetTimeEventChoiceFunUnit,
            ): TotalZoneStrategy {
                return getTotalZoneStrategy(
                    envZoneStrategy = envZoneStrategy,
                    renZoneStrategy = renZoneStrategy,
                    aGetTimeEventChoiceFun = { zs, unit ->
                        unit.getAdjuvantLHM(
                            false
                        )[aZSStringFun(zs)]!!.getRandomObj().toTimeEventChoice()
                    },
                    aGetTimeEventChoiceFunUnitFun = aGetTimeEventChoiceFunUnitFun,
                )
            }

            fun getType1Type1TotalZoneStrategy(
                envZoneStrategy: AgentZoneStrategy,
                renZoneStrategy: AgentZoneStrategy,
            ): TotalZoneStrategy {
                return getType1TotalZoneStrategy(
                    envZoneStrategy = envZoneStrategy,
                    renZoneStrategy = renZoneStrategy,
                    aZSStringFun = { zs ->
                        zs.getState0String()
                    },
                    aGetTimeEventChoiceFunUnitFun = { a1, a2 ->
                        getType1GetTimeEventChoiceFunUnit(
                            a1,
                            a2,
                        )
                    },
                )
            }
        }

        fun getMTimeEventChoice(
            config: Config,
        ): TimedEvent {
            return aGetTimeEventChoiceFunUnit.getTimeEventChoice(
                config
            )
        }

        override fun getMsTimeEventChoiceList(
            config: Config,
        ): ArrayList<TimedEvent> {
            return arrayListOf(getMTimeEventChoice(config))
        }
    }
}